﻿using System.Collections.Generic;
using System.IO;
using UnityEngine;

namespace CGF
{
    public static class TextureHelper
    {
        public static Dictionary<string, Texture> remoteTextureCache = new Dictionary<string, Texture>();

        /// <summary>
        /// 裁切多余部分变成正方形
        /// </summary>
        public static Texture2D CutTextureToSquare(Texture source)
        {
            int sourceW = source.width;
            int sourceH = source.height;
            if (sourceW > sourceH)
            {
                return CutTextureToSize(source, sourceH, sourceH);
            }
            else
            {
                return CutTextureToSize(source, sourceW, sourceW);
            }
        }

        /// <summary>
        /// 裁切图片成特定尺寸
        /// </summary>
        public static Texture2D CutTextureToSize(Texture source, int width, int height)
        {
            Texture2D source2D = source as Texture2D;
            float sourceW = source2D.width;
            float sourceH = source2D.height;
            if (sourceW == width && sourceH == height)
            {
                return source2D;
            }
            Texture2D result = null;
            if (sourceW / sourceH > width / (float)height)
            {
                if (sourceH < height)
                {
                    float ratio = width / (float)height;
                    height = (int)sourceH;
                    width = (int)(height * ratio);
                }
                result = new Texture2D(width, height, source2D.format, false);
                float sourceWScale = height * sourceW / sourceH;
                float offset = (sourceWScale - width) * 0.5f;
                for (int i = 0; i < height; ++i)
                {
                    for (int j = 0; j < width; ++j)
                    {
                        Color newColor = source2D.GetPixelBilinear((j + offset) / sourceWScale, i / (float)height);
                        result.SetPixel(j, i, newColor);
                    }
                }
            }
            else
            {
                if (sourceW < width)
                {
                    float ratio = height / (float)width;
                    width = (int)sourceW;
                    height = (int)(width * ratio);
                }
                result = new Texture2D(width, height, source2D.format, false);
                float sourceHScale = width * sourceH / sourceW;
                float offset = (sourceHScale - height) * 0.5f;
                for (int i = 0; i < height; ++i)
                {
                    for (int j = 0; j < width; ++j)
                    {
                        Color newColor = source2D.GetPixelBilinear(j / (float)width, (i + offset) / sourceHScale);
                        result.SetPixel(j, i, newColor);
                    }
                }
            }
            result.Apply();
            return result;
        }

        /// <summary>
        /// 压扁图片成特定尺寸
        /// </summary>
        public static Texture2D Resize(Texture source, int targetWidth, int targetHeight)
        {
            Texture2D source2D = source as Texture2D;
            Texture2D result = new Texture2D(targetWidth, targetHeight, source2D.format, false);
            for (int i = 0; i < result.height; ++i)
            {
                for (int j = 0; j < result.width; ++j)
                {
                    Color newColor = source2D.GetPixelBilinear(j / (float)result.width, i / (float)result.height);
                    result.SetPixel(j, i, newColor);
                }
            }
            result.Apply();
            return result;
        }
        
        /// <summary>
        /// 如果图片长的一边大于某个尺寸，就等比缩小图片
        /// </summary>
        public static Texture2D Resize(Texture2D sourceTexture, int maxSize)
        {
            int originalWidth = sourceTexture.width;
            int originalHeight = sourceTexture.height;
            int longestSide = Mathf.Max(originalWidth, originalHeight);
            if (longestSide <= maxSize)
            {
                //纹理尺寸无需缩放
                return sourceTexture;
            }
            int newWidth, newHeight;
            if (originalWidth > originalHeight)
            {
                newWidth = maxSize;
                newHeight = Mathf.RoundToInt((float)originalHeight / originalWidth * maxSize);
            }
            else
            {
                newHeight = maxSize;
                newWidth = Mathf.RoundToInt((float)originalWidth / originalHeight * maxSize);
            }
            Texture2D resizedTexture = new Texture2D(newWidth, newHeight, sourceTexture.format, false);
            Color[] pixels = resizedTexture.GetPixels(0);
            float ratioX = 1.0f / ((float)newWidth / (originalWidth - 1));
            float ratioY = 1.0f / ((float)newHeight / (originalHeight - 1));
            for (int y = 0; y < newHeight; y++)
            {
                int yFloor = Mathf.FloorToInt(y * ratioY);
                var y1 = yFloor * originalWidth;
                var y2 = (yFloor + 1) * originalWidth;
                var yw = y * newWidth;
                for (int x = 0; x < newWidth; x++)
                {
                    int xFloor = Mathf.FloorToInt(x * ratioX);
                    var xLerp = x * ratioX - xFloor;

                    pixels[yw + x] = Color.LerpUnclamped(
                        Color.LerpUnclamped(sourceTexture.GetPixel(xFloor, yFloor), sourceTexture.GetPixel(xFloor + 1, yFloor), xLerp),
                        Color.LerpUnclamped(sourceTexture.GetPixel(xFloor, yFloor + 1), sourceTexture.GetPixel(xFloor + 1, yFloor + 1), xLerp),
                        y * ratioY - yFloor);
                }
            }
            resizedTexture.SetPixels(pixels);
            resizedTexture.Apply();
            return resizedTexture;
        }

        /// <summary>
        /// 添加水印
        /// </summary>
        public static Texture2D AddWaterMarking(Texture2D texture, Texture2D marking)
        {
            int markW = (int)(texture.width * 0.5f);
            int markH = (int)(markW * marking.height / (float)marking.width);
            int offset = (int)(markH * 0.5f);
            int left = texture.width - markW - offset;
            int markXIndex = 0;
            int markYIndex = 0;
            for (int i = offset; i < offset + markH; i++)
            {
                markXIndex = 0;
                for (int j = left; j < left + markW; j++)
                {
                    Color bgColor = texture.GetPixel(j, i);
                    Color markColor = marking.GetPixelBilinear(markXIndex / (float)markW, markYIndex / (float)markH);
                    if (markColor.a > 0)
                    {
                        Color blendColor = Color.Lerp(bgColor, markColor, 0.5f);
                        texture.SetPixel(j, i, blendColor);
                    }
                    markXIndex++;
                }
                markYIndex++;
            }
            texture.Apply();
            return texture;
        }

        /// <summary>
        /// Base64 to Texture
        /// </summary>
        public static Texture2D BytesToTexture(byte[] bytes)
        {
            try
            {
                Texture2D tex = new Texture2D(2,2);
                tex.LoadImage(bytes);
                return tex;
            }
            catch (System.Exception ex)
            {
                Debug.LogError(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Base64 to Texture
        /// </summary>
        public static Texture2D Base64ToTexture(string base64)
        {
            if (base64.Contains(","))
            {
                string[] arr = base64.Split(',');
                base64 = arr[1];
            }
            try
            {
                Texture2D tex = new Texture2D(2,2);
                byte[] bytes = System.Convert.FromBase64String(base64);
                tex.LoadImage(bytes);
                return tex;
            }
            catch (System.Exception ex)
            {
                Debug.LogError(ex.Message);
            }
            return null;
        }

        /// <summary>
        /// Texture2D to Base64
        /// </summary>
        public static string TextureToBase64(Texture texture)
        {
            if (texture != null)
            {
                return System.Convert.ToBase64String(TextureToBytes(texture));
            }
            return null;
        }

        /// <summary>
        /// Texture转byte[]
        /// </summary>
        public static byte[] TextureToBytes(Texture texture)
        {
            if (texture.GetType() == typeof(WebCamTexture))
            {
                WebCamTexture webCamTexture = texture as WebCamTexture;
                Texture2D texture2D = new Texture2D(texture.width, texture.height);
                int y = 0;
                while (y < texture.height)
                {
                    int x = 0;
                    while (x < texture.width)
                    {
                        Color color = webCamTexture.GetPixel(x, y);
                        texture2D.SetPixel(x, y, color);
                        ++x;
                    }
                    ++y;
                }
                texture2D.Apply();
                return texture2D.EncodeToJPG();
            }
            else
            {
                return (texture as Texture2D).EncodeToJPG();
            }
        }

        /// <summary>
        /// Base64 to Byets
        /// </summary>
        public static byte[] Base64ToBytes(string base64)
        {
            if (!string.IsNullOrEmpty(base64))
            {
                return System.Convert.FromBase64String(base64);
            }
            return null;
        }

        /// <summary>
        /// Bytes to Base64
        /// </summary>
        public static string BytesToBase64(byte[] textureBytes)
        {
            if (textureBytes != null)
            {
                return System.Convert.ToBase64String(textureBytes);
            }
            return null;
        }

        /// <summary>
        /// Texture to Sprite
        /// </summary>
        public static Sprite TextureToSprite(Texture texture)
        {
            return Sprite.Create(texture as Texture2D, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
        }

        /// <summary>
        /// Change HashCode
        /// </summary>
        public static Texture2D ChangeMD5(Texture2D texture)
        {
            if (texture)
            {
                int texW = texture.width;
                int texH = texture.height;
                int offsetX = UnityEngine.Random.Range(0, texW);
                int offsetY = UnityEngine.Random.Range(0, texW);
                Color oldColor = texture.GetPixelBilinear(offsetX / (float)texW, offsetY / (float)texH);
                Color newColor = new Color(oldColor.r * 0.9f, oldColor.g * 0.9f, oldColor.b * 0.9f, oldColor.a * 0.9f);//改变一点点颜色
                texture.SetPixel(offsetX, offsetY, newColor);
                texture.Apply();
                return texture;
            }
            return null;
        }

        /// <summary>
        /// RenderTexture转Texture
        /// </summary>
        public static Texture2D RenderTextureToTexture2D(RenderTexture renderTexture)
        {
            if (!renderTexture) return null;
            RenderTexture prev = RenderTexture.active;
            RenderTexture.active = renderTexture;
            Texture2D texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
            texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
            RenderTexture.active = prev;
            return texture2D;
        }

        /// <summary>
		/// WebCamTexture转Texture
		/// </summary>
		public static Texture2D WebCamTextureToTexture2D(WebCamTexture renderTexture)
        {
            if (!renderTexture) return null;
            Texture2D texture2D = new Texture2D(renderTexture.width, renderTexture.height, TextureFormat.ARGB32, false);
            for (int y = 0; y < renderTexture.height; y++)
            {
                for (int x = 0; x < renderTexture.width; x++)
                {
                    texture2D.SetPixel(x, y, renderTexture.GetPixel(x, y));
                }
            }
            return texture2D;
        }

        /// <summary>
        /// Texture转RenderTexture
        /// </summary>
        public static RenderTexture Texture2DToRenderTexture(Texture texture)
        {
            RenderTexture rt = new RenderTexture((int)(texture.width * 0.5f), (int)(texture.height * 0.5f), 24);
            RenderTexture.active = rt;
            Graphics.Blit(texture, rt);
            return rt;
        }

        /// <summary>
        /// 同步读取本地图片
        /// </summary>
        public static Texture LoadTexture(string path)
        {
            if (!File.Exists(path))
            {
                Debug.LogError($"图片不存在:{path}");
                return null;
            }
            byte[] bytes = File.ReadAllBytes(path);
            return BytesToTexture(bytes);
        }

        /// <summary>
        /// 同步读取本地图片
        /// </summary>
        public static async CTask<Texture> LoadTextureRemote(string url, bool useCache = true)
        {
            if (useCache && remoteTextureCache.ContainsKey(url))
            {
                return remoteTextureCache[url];
            }
            HttpRes res = await HttpHelper.Get(url);
            if (res.success)
            {
                var texture = BytesToTexture(res.bytes);
                if (useCache && texture && !remoteTextureCache.ContainsKey(url))
                {
                    remoteTextureCache.Add(url, texture);
                }
                return texture;
            }
            return null;
        }

        /// <summary>
        /// 保存图片
        /// </summary>
        public static byte[] SaveTexture(Texture tex, string fileFullPath)
        {
            string dir = System.IO.Path.GetDirectoryName(fileFullPath);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            if (tex.GetType() == typeof(RenderTexture))
            {
                RenderTexture prev = RenderTexture.active;
                RenderTexture.active = tex as RenderTexture;
                Texture2D png = new Texture2D(tex.width, tex.height, TextureFormat.ARGB32, false);
                png.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
                byte[] bytes = fileFullPath.EndsWith(".png") ? png.EncodeToPNG() : png.EncodeToJPG();
                File.WriteAllBytes(fileFullPath, bytes);
                RenderTexture.active = prev;
                return bytes;
            }
            else
            {
                byte[] bytes = fileFullPath.EndsWith(".png") ? (tex as Texture2D).EncodeToPNG() : (tex as Texture2D).EncodeToJPG();
                File.WriteAllBytes(fileFullPath, bytes);
                return bytes;
            }
        }

    }
}